(function(global) {
var APP = global.APP, 
	Spine = global.Spine, 
	$ = global.$;

var _CLASSES = APP._classes,
	_BUTTONS = _CLASSES.buttons,
	_STATES = _CLASSES.states;

var FormHandler = function(options) {
	var _instances = FormHandler._instances || (FormHandler._instances = {}),
		elOrId = options.el, id = typeof elOrId === 'string' ? elOrId : elOrId.attr('id'),
		instance = _instances[id];

	if (!instance) {
		instance = new FormInstance(options);
		instance.bind('release', function() {
			var id = this.el.attr('id');
			_instances[id] = false;
		});
		_instances[id] = instance;
	}
	return instance;
};
APP.namespace('Modules.FormHandler', FormHandler);

var FormInstance = Spine.Controller.sub($.extend({}, APP.Tooltip, APP.Message, APP.MouseHandler));
FormInstance.elements = {
	"input:checkbox, input:radio, input:text, input:file, textarea, select" : "$fields",
	"input:submit" : "$submit"
};
FormInstance.events = {
	"mouseenter :input.ui-state-error" : "tooltip",
	"mouseenter label.ui-state-error" : "tooltip",
	"mouseenter :submit" : "highlight",
	"mouseleave :submit" : "highlight",
	"submit" : "submit",
	"click a[href=#reset]" : "reset"
};
// DONT USE INPUT[NAME SELECTOR BECAUSE IT MIGHT BE TEXTAREA OR SELECT, which are not input tags

FormInstance.prototype.init = function() {
	this.store();
	this.$submit.addClass(_BUTTONS.base);
};
FormInstance.prototype.tooltipHtml = function($target) {
	var html = '<div class="' + _STATES.error + '">' + $target.attr('title') + '</div>';
	return html;
};
FormInstance.prototype.store = function() {
	this._stored = this.serialize();
};
FormInstance.prototype.serialize = function() {
	var ret = [];
	this.$fields.each(function(index, element) {
		var $field = $(element), name = $field.attr('name'), value = '';

		if ($field.is('[type=hidden]')) {
			return;
		}
		if (($field.is(':checkbox') || $field.is(':radio')) && !$field.is(':checked')) {
			return;
		}
		value = $field.val();
		ret.push(name + '=' + value);
	});
	return ret.join(',');
};
FormInstance.prototype.validate = function() {
	return true;
};
FormInstance.prototype.submit = function(event) {
	var action = this.el.attr('action') + '.json',
		data, self = this;

	if (event) {
		event.preventDefault();
	}
	this.clearErrors();

	if (!this.validate()) {
		return;
	}

	data = this.el.serializeArray();
	this.disable();
	$.post(action, data, function(response) {
		if (response.expired) {
			alert('The form you are using has expired. The page will be reloaded.');
			window.location = response.redirect;
		}
		self.el.find('input:password').val('');
		self[response.success ? 'success' : 'error'](response);
		self.enable();
	});
}
FormInstance.prototype.fields = function(fields) {
	var selector;

	fields = typeof fields === 'string' ? fields.split(', ') : fields;
	selector = '[name="' + fields.join('"], [name="') + '"]';
	return this.el.find(selector);
}
FormInstance.prototype.success = function(response) {
//	console.log(response);
//	return;
	if (this._callbacks.success) {
		this.trigger('success', response);
		return;
	}
	if (response.redirect) {
		window.location = response.redirect;
		return;
	}
	if (response.message) {
		this.message(response.message);
	}
};
FormInstance.prototype.error = function(response) {
	var message = response.message, error, key, $field, $label;

	this.message(response.message, true);
	if (response.errors) {
		for (key in response.errors) {
			$field = this.el.find('[name="' + key + '"]:visible');
			$label = false;
			if ($field.is(':checkbox') || $field.is(':radio')) {
				$label = $field.siblings('label')
			}
			($label || $field)
				.addClass(_STATES.error)
				.attr('title', response.errors[key][0]);

			$field.one('change', function() {
				var $el = $(this);

				if ($el.hasClass(_STATES.error)) {
					$el.removeClass(_STATES.error)
						.removeAttr('title');
				} else {
					$el.siblings('label')
						.removeClass(_STATES.error)
						.removeAttr('title');
				}
			});
		}
	}
	this.$fields.filter('.' + _STATES.error).eq(0).focus();
	this.trigger('error', response);
};
FormInstance.prototype.clearErrors = function() {
	this.message();
	this.el.find('.' + _STATES.error)
		.removeClass(_STATES.error);
};
FormInstance.prototype.focus = function() {
	this.el.find('input:visible').eq(0).focus();
};
FormInstance.prototype.set = function(data) {
	var _key, $field, value;
	for (_key in data) {
		$field = this.el.find('[name="' + _key + '"]');
		if ($field.length === 0) {
			continue;
		}
		value = data[_key];
		if ($field.length === 1) {
			$field.val(value);
			continue;
		}
		if ($field.is(':checkbox')) {
			if (value) {
				$field.attr('checked', true);
			} else {
				$field.removeAttr('checked');
			}
			continue;
		}
		$field = $field.filter(':radio');
		$field.removeAttr('checked');
		$field.filter('[value="' + value + '"]').attr('checked', true);
	}
};
FormInstance.prototype.val = function(key) {
	return this.el.find('[name="' + key + '"]').val();
};
FormInstance.prototype.clear = function() {
	var _data = this._stored, parts = _data.split(','), current,
		data = {};

	while (parts.length) {
		current = parts.shift().split('=')[0];
		data[current] = '';
	}
	this.set(data);
	this.enable();
	this.clearErrors();
};
FormInstance.prototype.reset = function(event) {
	var _data = this._stored, parts = _data.split(','), current,
		data = {};

	if (event) {
		event.preventDefault();
	}
	while (parts.length) {
		current = parts.shift().split('=');
		data[current.shift()] = current.shift();
	}

	this.set(data);
	this.enable();
	this.clearErrors();
};
FormInstance.prototype.enable = function() {
	this.$submit.removeAttr('disabled')
		.removeClass(_STATES.disabled);
};
FormInstance.prototype.disable = function() {
	this.$submit.attr('disabled', true)
		.removeClass(_STATES.active + ' ' + _STATES.hover)
		.addClass(_STATES.disabled);
};
FormInstance.prototype.destroy = function() {
	this.release();
};
}) (this);